﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
namespace SLSocketServer
{
    public partial class MainForm : Form
    {
        LotteryWCF.LotteryServiceClient lsrv;
        System.Windows.Forms.Timer timer;
        #region 策略请求部分-公共变量
        const string POLICY_STRING = "<policy-file-request/>";//这个是固定的
        static Socket sktPolicy; // 服务端监听的 socket        
        static byte[] arrPolicyClient; //收到客户端发送来的策略字符串请求 缓冲区
        static int policyReceiveByteCount; // 接收到的信息字节数(用来辅助判断字符串最否接收完整)  
        static byte[] arrPolicyServerFile;//策略文件转化成的字节数组   
        #endregion

        #region 消息监听部分-公共变量
        static Socket sktServer;//服务端监听的 socket
        static List<ClientWrapper> lstClients = new List<ClientWrapper>(); // 客户端 Socket 列表
        Dictionary<string, ListViewItem> dicCilents = new Dictionary<string, ListViewItem>();
        #endregion

        public MainForm()
        {
            try
            {
                InitializeComponent();
                Thread tPolicy = new Thread(StartPolicyListen);
                tPolicy.Start();

                Thread tServer = new Thread(StartServerListen);
                tServer.Start();

                lsrv = WcfProxy.GetProxy;

                timer = new System.Windows.Forms.Timer();
                timer.Tick += new EventHandler(timer_Tick);
                timer.Interval = 60000;
                timer.Start();
            }
            catch (Exception ex)
            {
                WriteLog("MainForm", ex);
            }
        }

        void timer_Tick(object sender, EventArgs e)
        {
            try
            {
                foreach (ListViewItem item in lvClient.Items)
                {
                    ClientWrapper client = lstClients.Where(p => p.UserName == item.Text).First();
                    if(client!=null)
                        SendToClientByName(client, "9", client.UserName);
                }
            }
            catch (Exception ex)
            {
                WriteLog("清除列表失败!:", ex);
            }
        }
        #region 策略请求943端口监听

        /// <summary>
        /// 启动 PolicyServer
        /// </summary>
        private void StartPolicyListen()
        {
            try
            {
                //OutPut("(943端口)策略请求监听开始...");
                string policyFile = Path.Combine(Application.StartupPath, "Policy.xml");
                arrPolicyServerFile = File.ReadAllBytes(policyFile);

                // 初始化 socket ， 然后与端口绑定， 然后对端口进行监听
                sktPolicy = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                sktPolicy.Bind(new IPEndPoint(IPAddress.Any, 943)); // socket 请求策略文件使用 943 端口
                sktPolicy.Listen(100);

                // 开始接受客户端传入的连接
                sktPolicy.BeginAccept(new AsyncCallback(PolicyConnectComplate), null);
            }
            catch (Exception ex)
            {
                WriteLog("StartPolicyListen", ex);
            }
        }

        /// <summary>
        /// （循环调用）连接回调处理
        /// </summary>
        /// <param name="result"></param>
        private void PolicyConnectComplate(IAsyncResult result)
        {
            Socket sktClient = null; // 客户端发过来的 socket
            try
            {
                // 完成接受客户端传入的连接的这个异步操作，并返回客户端连入的 socket
                sktClient = sktPolicy.EndAccept(result);
            }
            catch (SocketException ex)
            {
                WriteLog("sktClient = sktPolicy.EndAccept(result)", ex);
            }

            arrPolicyClient = new byte[POLICY_STRING.Length];//设置策略请求接收缓冲区大小

            policyReceiveByteCount = 0;

            try
            {
                // 开始接收客户端传入的数据
                sktClient.BeginReceive(arrPolicyClient, 0, POLICY_STRING.Length, SocketFlags.None, new AsyncCallback(PolicyReceiveComplte), sktClient);
                sktPolicy.BeginAccept(new AsyncCallback(PolicyConnectComplate), null);
            }
            catch (Exception ex)
            {
                // socket 出错则关闭客户端 socket
                sktClient.Close();
                WriteLog("sktClient.BeginReceive", ex);
            }

            // 继续开始接受客户端传入的连接

        }


        /// <summary>
        /// 客户端策略字符串接收回调函数
        /// </summary>
        /// <param name="result"></param>
        private void PolicyReceiveComplte(IAsyncResult result)
        {
            Socket sktClient = result.AsyncState as Socket;

            try
            {
                // 完成接收数据的这个异步操作，并计算累计接收的数据的字节数
                policyReceiveByteCount += sktClient.EndReceive(result);

                if (policyReceiveByteCount < POLICY_STRING.Length)
                {
                    // 没有接收到完整的数据，则继续开始接收(循环调用)
                    sktClient.BeginReceive(arrPolicyClient, policyReceiveByteCount, POLICY_STRING.Length - policyReceiveByteCount, SocketFlags.None, new AsyncCallback(PolicyReceiveComplte), sktClient);
                    return;
                }

                // 把接收到的数据转换为字符串
                string _clientPolicyString = Encoding.UTF8.GetString(arrPolicyClient, 0, policyReceiveByteCount);

                if (_clientPolicyString.ToLower().Trim() != POLICY_STRING.ToLower().Trim())
                {
                    // 如果接收到的数据不是“<policy-file-request/>”，则关闭客户端 socket
                    sktClient.Close();
                    return;
                }

                // 开始向客户端发送策略文件的内容
                sktClient.BeginSend(arrPolicyServerFile, 0, arrPolicyServerFile.Length, SocketFlags.None, new AsyncCallback(PolicySendComplete), sktClient);
            }
            catch (Exception ex)
            {
                // socket 出错则关闭客户端 socket
                sktClient.Close();
                WriteLog("PolicyReceiveComplte", ex);
            }
        }

        /// <summary>
        /// 策划文件发送回调处理
        /// </summary>
        /// <param name="result"></param>
        private void PolicySendComplete(IAsyncResult result)
        {
            if (result.AsyncState == null)
                return;
            Socket sktClient = result.AsyncState as Socket;
            try
            {
                // 完成将信息发送到客户端的这个异步操作
                sktClient.EndSend(result);

                // 获取客户端的ip地址及端口号，并显示
                //OutPut("已回发策略文件到 " + sktClient.RemoteEndPoint.ToString());
            }
            catch (Exception ex)
            {
                WriteLog("PolicySendComplete", ex);
            }
            finally
            {
                // 关闭客户端 socket
                sktClient.Close();
            }
        }
        #endregion

        #region 客户端消息监听
        private void StartServerListen()
        {
            // 初始化 socket ， 然后与端口绑定， 然后对端口进行监听
            try
            {
                sktServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                sktServer.Bind(new IPEndPoint(IPAddress.Any, 4518)); //4502-4534范围
                sktServer.Listen(100);
                sktServer.BeginAccept(new AsyncCallback(ClientConnectComplete), null);
            }
            catch (Exception e)
            {
                WriteLog("StartServerListen", e);
            }
        }

        /// <summary>
        /// 客户端连接完成的回调函数
        /// </summary>
        /// <param name="async"></param>
        private void ClientConnectComplete(IAsyncResult async)
        {

            ClientWrapper client = new ClientWrapper();
            try
            {
                // 完成接受客户端传入的连接的这个异步操作，并返回客户端连入的 socket
                client.Socket = sktServer.EndAccept(async);

                // 将客户端连入的 Socket 放进客户端 Socket 列表
                lstClients.Add(client);
            }
            catch (Exception ex)
            {
                WriteLog("ClientConnectComplete===client.Socket", ex);
            }
            try
            {
                // 开始接收客户端传入的数据
                client.Socket.BeginReceive(client.Buffer, 0, client.Buffer.Length, SocketFlags.None, new AsyncCallback(ClientReceiveComplete), client);
            }
            catch (SocketException ex)
            {
                HandleException(client, ex);
            }
            try
            {
                sktServer.BeginAccept(new AsyncCallback(ClientConnectComplete), null);//继续接受下一个连接(关键！否则只能监听到第一个连接)
            }
            catch (SocketException ex)
            {
                HandleException(client, ex);
            }
        }


        /// <summary>
        /// 客户端数据接收回调函数
        /// </summary>
        /// <param name="async"></param>
        private void ClientReceiveComplete(IAsyncResult async)
        {
            ClientWrapper client = async.AsyncState as ClientWrapper;

            int _receiveCount = 0;//用于统计本次接收到的字节数

            try
            {
                // 完成接收数据的这个异步操作，并返回接收的字节数
                if (client.Socket.Connected)
                {
                    _receiveCount = client.Socket.EndReceive(async);
                }
                else
                {
                    lstClients.Remove(client);
                }
            }
            catch (SocketException ex)
            {
                HandleException(client, ex);
            }
            // 如果客户端连接没有断开，且所有字节都接收完成
            try
            {
                if (client.Socket.Connected && client.Socket.Available == 0)
                {
                    string[] clientStr = UTF8Encoding.UTF8.GetString(client.Buffer).Split(',');
                    switch (int.Parse(clientStr[0]))
                    {
                        case (int)CommandType.Login:
                            if (!dicCilents.ContainsKey(clientStr[1]))
                            {
                                string[] Values = { clientStr[1], client.Socket.RemoteEndPoint.ToString(), DateTime.Now.ToString() };
                                ListViewItem lvi = new ListViewItem(Values);
                                lvClient.Items.Add(lvi);
                                client.UserName = clientStr[1];
                                dicCilents.Add(clientStr[1], lvi);
                            }
                            else
                            {

                                ClientWrapper client0 = lstClients.Where(p => p.UserName == clientStr[1]).Last();
                                SendToClientByName(client0, "0", clientStr[1]);
                                client0.Socket.Close();
                                clearClients(client0);
                                //lvClient.Items.Remove(dicCilents[clientStr[1]]);
                                //dicCilents.Remove(clientStr[1]);
                                //lstClients.Remove(client0);
                                string[] Values = { clientStr[1], client.Socket.RemoteEndPoint.ToString(), DateTime.Now.ToString() };
                                ListViewItem lvi = new ListViewItem(Values);
                                lvClient.Items.Add(lvi);
                                client.UserName = clientStr[1];
                                dicCilents.Add(clientStr[1], lvi);
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                WriteLog("ClientReceiveComplete长串", ex);
            }
            try
            {
                //继续开始接收客户端传入的数据(关键！否则每个连接只能接受第一次发送过来的数据)
                if (client.Socket.Connected)
                {
                    client.Socket.BeginReceive(client.Buffer, 0, client.Buffer.Length, 0, new AsyncCallback(ClientReceiveComplete), client);
                }
            }
            catch (SocketException ex)
            {
                HandleException(client, ex);
            }
        }

        #region
        /// <summary>
        /// 发送数据到所有连入的客户端
        /// </summary>
        /// <param name="data">需要发送的数据</param>
        private void SendToClientByName(ClientWrapper client, string data, string UserName)
        {
            try
            {
                WriteLog(UserName);
                byte[] byteData = UTF8Encoding.UTF8.GetBytes(data);
                SendToClientByName(client, byteData, UserName);
            }
            catch (Exception ex)
            {
                clearClients(client);
                WriteLog("SendToClientByName", ex);
            }
        }

        /// <summary>
        /// 发送数据到所有连入的客户端
        /// </summary>
        /// <param name="data"></param>
        private void SendToClientByName(ClientWrapper client, byte[] data, string UserName)
        {
            try
            {
                if (client == null)
                {
                    WriteLog("SendToClientByName", "ClientWrapper的client为空!");
                }
            }
            catch (Exception ex)
            {
                WriteLog("SendToClientByName", ex);
            }
            if (client.Socket.Connected)
            {
                try
                {
                    // 如果某客户端 Socket 是连接状态，则向其发送数据
                    client.Socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendToClientComplete), client);
                }
                catch (SocketException ex)
                {
                    HandleException(client, ex);
                }
            }
            else
            {
                // 某 Socket 断开了连接的话则将其关闭，并将其清除出客户端 Socket 列表
                // 也就是说每次向所有客户端发送消息的时候，都会从客户端 Socket 集合中清除掉已经关闭了连接的 Socket
                try
                {
                    client.Socket.Close();
                    lvClient.Items.Remove(dicCilents[UserName]);
                    dicCilents.Remove(UserName);
                    lstClients.Remove(client);
                    lsrv.LoginOut(UserName);
                    WriteLog(string.Format("客户端{0}关闭了连接正常退出！", client.UserName));
                }
                catch (Exception e)
                {
                    WriteLog("SendToClientByName=== client.Socket.Close&lsrv.LoginOut", e);
                }
            }
        }
        #endregion

        private void SendToClientComplete(IAsyncResult async)
        {
            ClientWrapper client = async.AsyncState as ClientWrapper;
            try
            {
                // 完成将信息发送到客户端的这个异步操作
                if (client.Socket.Connected)
                {
                    client.Socket.EndSend(async);
                }
                else
                {
                    clearClients(client);
                }
            }
            catch (SocketException ex)
            {
                HandleException(client, ex);
            }
        }
        private void clearClients(ClientWrapper client)
        {
            if (lvClient.Items.Contains(dicCilents[client.UserName]))
            {
                lvClient.Items.Remove(dicCilents[client.UserName]);
            }
            if (dicCilents.ContainsKey(client.UserName))
            {
                dicCilents.Remove(client.UserName);
            }
            if (lstClients.Contains(client))
            {
                lstClients.Remove(client);
            }
        }
        /// <summary>
        /// 处理 SocketException 异常
        /// </summary>
        /// <param name="client">导致异常的 ClientSocketPacket</param>
        /// <param name="ex">SocketException</param>
        private void HandleException(ClientWrapper client, SocketException ex)
        {
            try
            {
                // 在服务端记录异常信息，关闭导致异常的 Socket，并将其清除出客户端 Socket 列表           
                //OutPut("异常：" + client.Socket.RemoteEndPoint.ToString() + " - " + ex.Message);
                client.Socket.Close();
                lsrv.LoginOut(client.UserName);
                WriteLog(string.Format("客户端{0}异常退出！", client.UserName));
                clearClients(client);
            }
            catch (Exception e)
            {
                WriteLog("HandleException", e);
            }
        }
        #endregion
        public void WriteLog(Exception ex)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + "log.txt";
            if (!File.Exists(path))
                File.CreateText(path);
            StreamWriter sw = new StreamWriter(new FileStream(path, FileMode.Append));
            sw.WriteLine("Time:" + DateTime.Now.ToString() + ",Exception:" + ex.Message);
            sw.Flush();
            sw.Close();
        }
        public void WriteLog(string userName)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + "log.txt";
            if (!File.Exists(path))
                File.CreateText(path);
            StreamWriter sw = new StreamWriter(new FileStream(path, FileMode.Append));
            sw.WriteLine("LoginTime:" + DateTime.Now.ToString() + ",LoginUser:" + userName);
            sw.Flush();
            sw.Close();
        }
        public void WriteLog(string title, string ex)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + "log.txt";
            if (!File.Exists(path))
                File.CreateText(path);
            StreamWriter sw = new StreamWriter(new FileStream(path, FileMode.Append));
            sw.WriteLine("Time:" + DateTime.Now.ToString() + ",Title:" + title + ",Exception:" + ex);
            sw.Flush();
            sw.Close();
        }
        public void WriteLog(string title, Exception ex)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + "log.txt";
            if (!File.Exists(path))
                File.CreateText(path);
            StreamWriter sw = new StreamWriter(new FileStream(path, FileMode.Append));
            sw.WriteLine("Time:" + DateTime.Now.ToString() + ",Title:" + title + ",Exception:" + ex.Message + "+,StackTrace:" + ex.StackTrace);
            sw.Flush();
            sw.Close();
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Application.Exit();
        }
    }
}
